using Cinemachine;
using Cinemachine.Utility;
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using static Fxb.CinemachineEx.SimpleHeading;

namespace Fxb.CinemachineEx
{
    /// <summary>
    /// 基于v2 
    /// 解决bug:
    /// 1.初始方向改为forward,v2版为-forward。
    /// 2.增加 heading 参数，此参数为 TargetForwardWithWorldUp 时可以基于被观察物forawrd来进行旋转，防止被观察物体位置移动后出现bug。
    /// 3.额外增加 inheritFollowDir 参数，继承上一个镜头的跟随姿态
    /// </summary>
    public class AroundOrbitalTransposerV3 : CinemachineComponentBase
    {
        public override bool IsValid => enabled && FollowTarget != null;

        public override CinemachineCore.Stage Stage => CinemachineCore.Stage.Body;
         
        private (float hAxisVal, float vAxisVal)? initAxisValCache;

        [Tooltip("是否继承上一个镜头的跟随目标方向，类似v1版的效果。 优先级高过 m_InheritPosition")]
        public bool inheritFollowDir = true;

        public SimpleHeading heading;

        [Tooltip("默认离目标的距离")]
        public float distanceToTarget = 0.5f;

        [Tooltip("左右轴旋转")]
        [AxisStateProperty]
        public AxisState hAxis = new AxisState(-180, 180, true, false, 200f, 0.1f, 0.1f, "Horizontal", false);

        [Tooltip("上下轴旋转")]
        [AxisStateProperty]
        public AxisState vAxis = new AxisState(-85, 85, false, false, 200f, 0.1f, 0.1f, "Vertical", true);

        //目前需求应该每次重新激活镜头都需要恢复到默认的位置
        // [Tooltip("是否缓存初始的Axis val，为true则在OnEnable中重置Axis val到初始值")]
        // public bool cacheEntranceAxisVals;

        public override void OnTargetObjectWarped(Transform target, Vector3 positionDelta)
        {
            base.OnTargetObjectWarped(target, positionDelta);

            Debug.Log("OnTargetObjectWarped");

            Debug.Break();
        }

        private void OnEnable()
        {
            UpdateInputAxisProvider();

            if (initAxisValCache == null)
            {
                initAxisValCache = (hAxis.Value, vAxis.Value);
            }
        }

        /// <summary>
        /// API for the inspector.  Internal use only
        /// </summary>
        public void UpdateInputAxisProvider()
        {
            hAxis.SetInputAxisProvider(0, null);
            vAxis.SetInputAxisProvider(1, null);
            if (VirtualCamera != null)
            {
                var provider = VirtualCamera.GetInputAxisProvider();
                if (provider != null)
                {
                    hAxis.SetInputAxisProvider(0, provider);
                    vAxis.SetInputAxisProvider(1, provider);
                }
            }
        }

        public override bool OnTransitionFromCamera(ICinemachineCamera fromCam, Vector3 worldUp, float deltaTime, ref CinemachineVirtualCameraBase.TransitionParams transitionParams)
        {
            if (deltaTime < 0)
                return false;

            hAxis.Reset();

            vAxis.Reset();

            var needReUpdate = false;

            if(fromCam != null)
            {
                Vector3 targetToRawDir = default;

                if(inheritFollowDir && fromCam.Follow != null && fromCam.Follow != FollowTarget)
                {
                    targetToRawDir = (fromCam.State.RawPosition - fromCam.Follow.position).normalized;
                }
                else if(transitionParams.m_InheritPosition)
                {
                    targetToRawDir = (fromCam.State.RawPosition - FollowTargetPosition).normalized;
                }

                if(targetToRawDir != default)
                {
                    var headingFwd = Vector3.forward;

                    switch (heading.definition)
                    {
                        case HeadingDefinition.TargetForwardWithTargetUp:
                        case HeadingDefinition.TargetForwardWithWorldUp:
                            headingFwd = FollowTarget.forward.ProjectOntoPlane(worldUp);
                            break;
                    }

                    var (hAxisVal, vAxisVal) = CalcAxisVals(headingFwd, worldUp, targetToRawDir);

                    hAxis.Value = hAxisVal;

                    vAxis.Value = vAxisVal;

                    needReUpdate = true;
                }
            }
 
            if(!needReUpdate)
            {
                if (initAxisValCache != null)
                {
                    hAxis.Value = initAxisValCache.Value.hAxisVal;

                    vAxis.Value = initAxisValCache.Value.vAxisVal;
                }
            }
             
            return needReUpdate;
        }

        public override void MutateCameraState(ref CameraState curState, float deltaTime)
        {
            if (IsValid && deltaTime > 0)
            {
                if(CinemachineCore.Instance.IsLive(VirtualCamera))
                {
                    //delta time小于0不应该更新axis
                    hAxis.Update(deltaTime);

                    vAxis.Update(deltaTime);
                }
                else
                {
                    //运行时切换到未激活状态后不再影响当前状态
                    hAxis.Reset();

                    vAxis.Reset();
                }
            }
            var headingFwd = Vector3.forward;

            var upward = curState.ReferenceUp;

            switch (heading.definition)
            {
                case HeadingDefinition.TargetForwardWithWorldUp:
                    headingFwd = FollowTarget.forward.ProjectOntoPlane(curState.ReferenceUp);
                    break;
                case HeadingDefinition.TargetForwardWithTargetUp:
                    curState.ReferenceUp = upward = FollowTarget.up;

                    headingFwd = FollowTarget.forward;
                    break;
            }

            var targetToRaw = CalcTargetToRawDir(headingFwd, upward, hAxis.Value, vAxis.Value);

            curState.RawPosition = FollowTargetPosition + targetToRaw * distanceToTarget;
             
            //test
            // CalcAxisVals(headingFwd, curState.ReferenceUp, targetToRaw);
        }

        /// <summary>
        /// 计算合适的h/v val
        /// </summary>
        /// <param name="worldUP">上方向</param>
        /// <param name="targetToRawDir">以观察目标为中心到观察者的向量</param>
        /// <returns></returns>
        protected (float hAxisVal, float vAxisVal) CalcAxisVals(Vector3 headingFwd, Vector3 worldUP, Vector3 targetToRawDir)
        {
            var fwd = headingFwd;

            var observeFwd = -targetToRawDir;

            var xyRotInvert = Quaternion.FromToRotation(fwd, observeFwd);

            var fwdInvert = xyRotInvert * fwd;

            var upInvert = xyRotInvert * worldUP;

            var rightInvert = Vector3.Cross(upInvert, fwdInvert);

            var rot = Quaternion.FromToRotation(fwd, Vector3.forward) * Quaternion.LookRotation(fwdInvert, upInvert);

            // Debug.DrawRay(VirtualCamera.transform.position, fwd, Color.yellow);

            // Debug.DrawRay(VirtualCamera.transform.position,  observeFwd, Color.red);

            // Debug.DrawRay(VirtualCamera.transform.position, fwdInvert, Color.green);

            // Debug.DrawRay(VirtualCamera.transform.position, upInvert, Color.black);

            // Debug.DrawRay(VirtualCamera.transform.position, rightInvert, Color.blue);

            var res = (hAxis: NormalizeAngle(rot.eulerAngles.y),  vAxis: NormalizeAngle(rot.eulerAngles.x));

            // Debug.Log($"CalcAxisVals  res: {res}");

            return res;
        }

        /// <summary>
        /// 计算以观察目标为中心的旋转后的向量
        /// </summary>
        /// <param name="worldUP">空间上方向</param>
        /// <param name="hAxisVal">hAxis的值</param>
        /// <param name="vAxisVal">vAxis的值</param>
        /// <returns></returns>
        protected Vector3 CalcTargetToRawDir(Vector3 headingFwd, Vector3 worldUP, float hAxisVal, float vAxisVal)
        {
            //Debug.DrawRay(FollowTarget.position, worldUP, Color.yellow);
 
            var fwd = headingFwd;

            //横向旋转
            var xRot = Quaternion.AngleAxis(hAxisVal, worldUP);

            //横向旋转后的右方向。（纵向旋转轴）
            var right = xRot * Vector3.Cross(worldUP, fwd);

            //Debug.DrawRay(FollowTarget.position, right, Color.yellow);
             
            //纵向旋转
            var yRot = Quaternion.AngleAxis(vAxisVal, right);

            var xyRot = yRot * xRot;

            //观察方向 镜头->物体
            var observeFwd = (xyRot * fwd).normalized;

            //Debug.DrawRay(FollowTarget.position, -observeFwd, Color.red);

            return -observeFwd;
        }

        static float NormalizeAngle(float angle)
        {
            return ((angle + 180) % 360) - 180;
        }
    }
}